/* 
 * Enterprise Library Extensions - DI + ORM.
 * Copyright (C) 2006
 *
 * GNU General Public License
 *
 * This program is free software; you can redistribute 
 * it and/or modify it under the terms of the GNU General Public License 
 * as published by the Free Software Foundation; either version 2 of the 
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but 
 * WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
 * See the GNU General Public License for more details.
 */
using System;
using System.Data;
using System.Data.Common;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using Microsoft.Practices.EnterpriseLibrary.Data;
using ELE.EnterpriseLibrary.Properties;
using System.Configuration;

namespace ELE.EnterpriseLibrary.Data
{
  /// <summary>
  /// Implementation of the <see cref="IObjectQueryRunner"/> used by the <see cref="ObjectStorage"/> 
  /// to run sql queries based on that storage's queries and mappings.
  /// </summary>
  public class ObjectQueryManager : ObjectQueryRunnerBase
  {
    private ObjectStorage store;
    /// <summary>
    /// The constructor must reference the container that runs this instance.
    /// </summary>
    /// <param name="storage">Container</param>
    public ObjectQueryManager(ObjectStorage storage)
    {
      store = storage;
    }

    #region Mapping helper methods
    /// <summary>
    /// Resolves inner queries by looking and matching the mapping in the objectQuery to
    /// the mappings defined in the <see cref="ObjectStorage"/> container.
    /// </summary>
    /// <param name="objectQuery">The query object.</param>
    /// <returns>The populated query object.</returns>
    protected ObjectSqlQuery ResolveQueryMappings(ObjectSqlQuery objectQuery)
    {
      ObjectSqlQuery query = (ObjectSqlQuery)objectQuery.Clone();
      query.Mappings = new List<ObjectSqlMapping>();
      foreach (ObjectSqlMapping map in objectQuery.Mappings)
      {
        ObjectSqlMapping objectMapping = (ObjectSqlMapping)this.GetMapping(map.MapName, map.Name).Clone();
        objectMapping.MapName = map.MapName;
        this.OverwriteMapping(objectMapping, map);
        query.Mappings.Add((ObjectSqlMapping)objectMapping.Clone());
       
      }
      return query;
    }
    /// <summary>
    /// Resolves inner queries by looking and matching the mapping in the objectQuery to
    /// the mappings defined in the <see cref="ObjectStorage"/> container.
    /// </summary>
    /// <param name="objectStoredProcedure">The query object.</param>
    /// <returns>The populated query object.</returns>
    protected ObjectStoredProcedure ResolveStoredProcedureMappings(ObjectStoredProcedure objectStoredProcedure)
    {
      ObjectStoredProcedure storedProcedure = (ObjectStoredProcedure)objectStoredProcedure.Clone();
      storedProcedure.Mappings = new List<ObjectSqlMapping>();
      foreach (ObjectSqlMapping map in objectStoredProcedure.Mappings)
      {
        ObjectSqlMapping objectMapping = (ObjectSqlMapping)this.GetMapping(map.MapName, map.Name).Clone();
        objectMapping.MapName = map.MapName;
        this.OverwriteMapping(objectMapping, map);
        storedProcedure.Mappings.Add((ObjectSqlMapping)objectMapping.Clone());

      }
      storedProcedure.Cursors = new List<ObjectStoredProcedureCursor>();
      if (objectStoredProcedure.Cursors != null)
      {
        foreach (ObjectStoredProcedureCursor cursor in objectStoredProcedure.Cursors)
        {
          ObjectStoredProcedureCursor sCursor = new ObjectStoredProcedureCursor(cursor.Name);
          sCursor.Name = cursor.Name;
          sCursor.Mappings = new List<ObjectSqlMapping>();
          foreach(ObjectSqlMapping map in cursor.Mappings)
          {
            ObjectSqlMapping objectMapping = (ObjectSqlMapping)this.GetMapping(map.MapName, map.Name).Clone();
            objectMapping.MapName = map.MapName;
            this.OverwriteMapping(objectMapping, map);
            sCursor.Mappings.Add((ObjectSqlMapping)objectMapping.Clone());
          }
        }
      }
      return storedProcedure;
    }
    #endregion

    #region General helper methods
    /// <summary>
    /// Gets a stored procedure by name
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    public virtual ObjectStoredProcedure GetStoredProcedure(string id)
    {
      if (string.IsNullOrEmpty(id))
      {
        //return store.GetQuery(store.DefaultQueryName);
        return null;
      }
      else
      {
        return store.GetStoredProcedure(id);
      }
    }
    /// <summary>
    /// Gets a query from the <see cref="ObjectStorage"/> container of this instance.
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    public virtual ObjectSqlQuery GetQuery(string id)
    {
      if (string.IsNullOrEmpty(id))
      {
        return store.GetQuery(store.DefaultQueryName);
      }
      else
      {
        return store.GetQuery(id);
      }
    }
    /// <summary>
    /// Gets the set of mappings from the <see cref="ObjectStorage"/> container identified by a given id.
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    public virtual ObjectSqlMappingSet GetMappingSet(string id)
    {
      if (string.IsNullOrEmpty(id))
      {
        return store.GetMappingSet(store.DefaultMappingName);
      }
      else
      {
        return store.GetMappingSet(id);
      }
    }
    /// <summary>
    /// Retrieves a specific <see cref="ObjectSqlMapping"/> with the given objectId 
    /// from a <see cref="ObjectSqlMappingManager"/> created by
    /// the <see cref="ObjectSqlFactory"/> that is identified by the given mappingId.
    /// </summary>
    /// <param name="mappingId"></param>
    /// <param name="objectId"></param>
    /// <returns></returns>
    public override ObjectSqlMapping GetMapping(string mappingId, string objectId)
    {
      ObjectSqlMappingSet set = this.GetMappingSet(mappingId);
      return set.GetObjectMapping(objectId);
    }
    #endregion

    #region Select query helper methods

    /// <summary>
    /// Helper method to execute a query.
    /// </summary>
    /// <param name="database"></param>
    /// <param name="queryId"></param>
    /// <param name="inputObject"></param>
    /// <param name="recursive"></param>
    /// <returns></returns>
    protected override IList ExecuteQueryByNameHelper(Database database, string queryId, object inputObject, bool recursive)
    {
      IList results = new ArrayList();
      ObjectSqlQuery query = this.ResolveQueryMappings(this.GetQuery(queryId));
      IDataReader reader = this.ExecuteDatareaderQueryByNameHelper(database, query, inputObject);
      while (reader.Read())
      {
        foreach (ObjectSqlMapping mapping in query.Mappings)
        {
          results.Add(this.GetObjectFromReaderPosition(database, reader, mapping, recursive));
        }
      }
      return results;
    }
    #endregion

    #region IObjectQueryRunner Members
    /// <summary>
    /// Executes a stored procedure returning a data set
    /// </summary>
    /// <param name="database"></param>
    /// <param name="storedProcedureId"></param>
    /// <param name="inputObject"></param>
    /// <returns></returns>
    public override DataSet ExecuteDatasetStoredProcedureByName(Database database, string storedProcedureId, object inputObject)
    {
      ObjectStoredProcedure storedProcedure = this.ResolveStoredProcedureMappings(this.GetStoredProcedure(storedProcedureId));
      using (DbCommand command = this.PrepareStoredProcedureCommandInputs(database, storedProcedure, inputObject))
      {
        DataSet resultDataSet = null;

        resultDataSet = database.ExecuteDataSet(command);

        // Note: connection was closed by ExecuteDataSet method call 

        return resultDataSet;
      }
    }
    /// <summary>
    /// Executes a stored procedure returning a list of mapped objects
    /// </summary>
    /// <param name="database"></param>
    /// <param name="storedProcedureId"></param>
    /// <param name="inputObject"></param>
    /// <param name="recursive"></param>
    /// <returns></returns>
    protected override IList ExecuteStoredProcedureByNameHelper(Database database, string storedProcedureId, object inputObject, bool recursive)
    {
      throw new NotImplementedException("The method or operation is not implemented.");
    }
    /// <summary>
    /// Implementation of the interface method.
    /// See <see cref="IObjectQueryRunner.ExecuteDatasetQueryByName(Database, string, object)"/> for more information.
    /// </summary>
    /// <param name="database"></param>
    /// <param name="queryId"></param>
    /// <param name="inputObject"></param>
    /// <returns></returns>
    public override DataSet ExecuteDatasetQueryByName(Database database, string queryId, object inputObject)
    {
      ObjectSqlQuery query = this.ResolveQueryMappings(this.GetQuery(queryId));
      using (DbCommand command = this.PrepareCommandInputs(database, query, inputObject))
      {
        DataSet resultDataSet = null;

        resultDataSet = database.ExecuteDataSet(command);

        // Note: connection was closed by ExecuteDataSet method call 

        return resultDataSet;
      }
    }
    /// <summary>
    /// Implementation of the interface method.
    /// See <see cref="IObjectQueryRunner.ExecuteDatareaderQueryByName(Database, string, object)"/> for more information.
    /// </summary>
    /// <param name="database"></param>
    /// <param name="queryId"></param>
    /// <param name="inputObject"></param>
    /// <returns></returns>
    public override IDataReader ExecuteDatareaderQueryByName(Database database, string queryId, object inputObject)
    {
      ObjectSqlQuery query = this.ResolveQueryMappings(this.GetQuery(queryId));
      return this.ExecuteDatareaderQueryByNameHelper(database, query, inputObject);
    }
    #endregion
  }
}
